package com.bckj.fastboot.core.redisson;

import com.bckj.fastboot.core.lang.func.Func0;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RedissonClient;

import java.util.concurrent.TimeUnit;

@Slf4j
@RequiredArgsConstructor
public class RLockOps {

    private final RedissonClient redissonClient;

    /**
     * 不考虑锁释放而业务未执行完成情况（使用看门狗）
     * @param func0 业务方法
     * @param lockName 锁名称
     * @param waitTime 等待时间
     * @return
     * @param <T>
     */
    public <T> T tryLock(Func0<T> func0, String lockName, long waitTime) {
        return tryLock(func0, lockName, waitTime, -1);
    }

    /**
     * 尝试在等待时间内获取分布式锁
     * @param func0 业务方法
     * @param lockName 锁名称
     * @param waitTime 等待时间
     * @param releaseTime 锁释放时间
     * @return
     * @param <T>
     */
    public <T> T tryLock(Func0<T> func0, String lockName, long waitTime, long releaseTime) {
        var lock = redissonClient.getLock(lockName);
        try {
            if (lock.tryLock(waitTime, releaseTime, TimeUnit.SECONDS)) {
                return func0.call();
            }
            log.info("获取分布式锁失败,锁key:{},等待时间:{},释放时间:{}", lockName, waitTime, releaseTime);
            throw new RuntimeException("请求频繁，请稍后再试");
        } catch (InterruptedException e) {
            log.error("线程阻塞,获取分布式锁失败,锁key:{},等待时间:{},释放时间:{}", lockName, waitTime, releaseTime);
            throw new RuntimeException("请求频繁，请稍后再试");
        } finally {
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    public <T> T lock(Func0<T> func0, String lockName) {
        return lock(func0, lockName, -1);
    }

    public <T> T lock(Func0<T> func0, String lockName, long releaseTime) {
        var lock = redissonClient.getLock(lockName);
        try {
            lock.lock(releaseTime, TimeUnit.SECONDS);
            return func0.call();
        } finally {
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }
}
